package net.jxta.impl.util.threads;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * ScheduledExecutorService implementation which performs the scheduling of
 * the tasks but then uses another ExecutorService implementation to actually
 * execute tasks.  This allows for small, purpose-driven scheduled executors
 * to be created which then delegate to the more common application-level
 * thread pools.
 * 
 * NOTE: This class is dynamically patched at build time by ant to support the 
 * java 1.6 semantics for invokeAny and invokeAll if 1.6 is found.  See the discussion
 * beginning at https://jxta.dev.java.net/servlets/ReadMsg?list=dev&msgNo=981
 * 
 */
public class ProxiedScheduledExecutorService
        implements ScheduledExecutorService {
    
    /**
     * ScheduledExecutorService instance used for task scheduling.
     */
    private final ScheduledExecutorService schedExec;
    
    /**
     * ExecutorService used when we actually need to execute a task.
     */
    private final ExecutorService targetExec;
    
    /**
     * Flag indicating whether or not shutdown commands should be forwarded
     * on to the wrapped ScheduledExecutorService instance.
     */
    private final boolean forwardShutdown;

    ///////////////////////////////////////////////////////////////////////////
    // Constructors:
    
    /**
     * Creates a scheduled executor service which executes all tasks on the
     * specified executor service.  This form creates an internal single
     * thread scheduled executor for scheduling purposes.
     * 
     * @param targetExecutor executor service instance to use for task
     *  execution
     */
    public ProxiedScheduledExecutorService(final String name,
            final ExecutorService targetExecutor) {
        this(Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(name + "-Local")),
                targetExecutor, true);
    }
    
    /**
     * Creates a scheduled executor service which executes all tasks on the
     * specified executor service.  This form creates uses the provided
     * scheduled executor service instance for scheduling operations but
     * prevents the shutdown commands from impacting the underlying
     * scheduled executor service instance.
     * 
     * @param schedExecutor scheduled executor service instance to use for
     *  scheduling tasks
     * @param targetExecutor executor service instance to use for task
     *  execution
     */
    public ProxiedScheduledExecutorService(
            final ScheduledExecutorService schedExecutor,
            final ExecutorService targetExecutor) {
        this(schedExecutor, targetExecutor, false);
    }
    
    /**
     * Creates a scheduled executor service which executes all tasks on the
     * specified executor service.  This form creates uses the provided
     * scheduled executor service instance for scheduling operations but
     * prevents the shutdown commands from impacting the underlying
     * scheduled executor service instance.
     * 
     * @param schedExecutor scheduled executor service instance to use for
     *  scheduling tasks
     * @param targetExecutor executor service instance to use for task
     *  execution
     * @param forwardShutdownCommands flag indicating whether or not the
     *  shutdown commands should impact the underlying scheduled executor
     *  service instance.  <code>true</code> if a call to
     *  <code>shutdown()</code> or <code>shutdownNow()</code> should result
     *  in the corresponding methods of the wrapped scheduled executor service
     *  instance being called, <code>false</code> otherwise.
     */
    public ProxiedScheduledExecutorService(
            final ScheduledExecutorService schedExecutor,
            final ExecutorService targetExecutor,
            final boolean forwardShutdownCommands) {
        schedExec = schedExecutor;
        targetExec = targetExecutor;
        forwardShutdown = forwardShutdownCommands;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    // ScheduledExecutorService interface methods:
    
    /**
     * {@inheritDoc}
     * 
     * This implementation wraps the provided Runnable in a proxy object which
     * then forwards the execution on to the target executor service instance
     * for execution.
     */
    public ScheduledFuture<?> schedule(
            final Runnable command,
            final long delay,
            final TimeUnit unit) {
        ProxiedRunnable proxy = new ProxiedRunnable(targetExec, command);
        return schedExec.schedule(proxy, delay, unit);
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation wraps the provided Callable in a proxy object which
     * then forwards the execution on to the target executor service instance
     * for execution.
     * 
     * @param <V> return type of the function
     */
    public <V> ScheduledFuture<V> schedule(
            final Callable<V> callable,
            final long delay,
            final TimeUnit unit) {
        ProxiedCallable<V> proxy = new ProxiedCallable<V>(targetExec, callable);
        ScheduledFuture<V> schedFuture = schedExec.schedule(proxy, delay, unit);
        ProxiedScheduledFuture<V> proxyFuture =
                new ProxiedScheduledFuture<V>(schedFuture);
        proxy.setProxiedScheduledFuture(proxyFuture);
        return proxyFuture;
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation wraps the provided Runnable in a proxy object which
     * then forwards the execution on to the target executor service instance
     * for execution.
     */
    public ScheduledFuture<?> scheduleAtFixedRate(
            final Runnable command,
            final long initialDelay,
            final long period,
            final TimeUnit unit) {
        ProxiedRunnable proxy = new ProxiedRunnable(targetExec, command);
        return schedExec.scheduleAtFixedRate(
                proxy, initialDelay, period, unit);
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation wraps the provided Runnable in a proxy object which
     * then forwards the execution on to the target executor service instance
     * for execution.
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(
            final Runnable command,
            final long initialDelay,
            final long delay,
            final TimeUnit unit) {
        ProxiedRunnable proxy = new ProxiedRunnable(targetExec, command);
        return schedExec.scheduleWithFixedDelay(
                proxy, initialDelay, delay, unit);
    }

    ///////////////////////////////////////////////////////////////////////////
    // ExecutorService interface methods:
    
    /**
     * {@inheritDoc}
     * 
     * This implementation will call <code>shutdown()</code> on the wrapped
     * ScheduledExecutorService if requested during construction.  Otherwise,
     * it does nothing.
     */
    public void shutdown() {
        if (forwardShutdown) {
            schedExec.shutdown();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation will call <code>shutdownNow()</code> on the wrapped
     * ScheduledExecutorService if requested during construction.  Otherwise,
     * it always do nothing and return an empty list.
     */
    public List<Runnable> shutdownNow() {
        if (forwardShutdown) {
            return schedExec.shutdownNow();
        } else {
            return Collections.emptyList();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation is equivalent to calling <code>isShutdown()</code>
     * on the wrapped ScheduledExecutorService.
     */
    public boolean isShutdown() {
        return schedExec.isShutdown();
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation is equivalent to calling <code>isShutdown()</code>
     * on the wrapped ScheduledExecutorService.
     */
    public boolean isTerminated() {
        return schedExec.isTerminated();
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation is equivalent to calling <code>isShutdown()</code>
     * on the wrapped ScheduledExecutorService.
     * 
     * @throws InterruptedException if interrupted while waiting
     */
    public boolean awaitTermination(
            final long timeout,
            final TimeUnit unit)
            throws InterruptedException {
        return schedExec.awaitTermination(timeout, unit);
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation is equivalent to calling the equivalent
     * <code>submit()</code> method on the wrapped ExecutorService instance.
     * 
     * @param <T> return type of the function
     */
    public <T> Future<T> submit(final Callable<T> task) {
        return targetExec.submit(task);
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation is equivalent to calling the equivalent
     * <code>submit()</code> method on the wrapped ExecutorService instance.
     * 
     * @param <T> return type of the function
     */
    public <T> Future<T> submit(final Runnable task, final T result) {
        return targetExec.submit(task, result);
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation is equivalent to calling the equivalent
     * <code>submit()</code> method on the wrapped ExecutorService instance.
     */
    public Future<?> submit(final Runnable task) {
        return targetExec.submit(task);
    }








    /**
     * {@inheritDoc}
     */
    public void execute(final Runnable command) {
        targetExec.execute(command);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Public methods:
    
    /**
     * {@inheritDoc}
     * 
     * This implementation calls <code>shutdownNow()</code>.
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        shutdownNow();
    }

    /**
     * {@inheritDoc}
     * 
     * This implementation is equivalent to calling the equivalent
     * <code>invokeAll()</code> method on the wrapped ExecutorService instance.
     * 
     * @param <T> return type of the function(s)
     * @throws InterruptedException if interrupted while waiting, in which
     *  case unfinished tasks are cancelled 
     */

	public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
			throws InterruptedException {
		return targetExec.invokeAll(tasks);
	}

	  /**
	  * {@inheritDoc}
	  * 
	  * This implementation is equivalent to calling the equivalent
	  * <code>invokeAll()</code> method on the wrapped ExecutorService instance.
	  * 
	  * @param <T> return type of the function(s)
	  * @throws InterruptedException if interrupted while waiting, in which
	  *  case unfinished tasks are cancelled 
	  */
	public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, 
			long timeout, TimeUnit unit) throws InterruptedException {
		return targetExec.invokeAll(tasks, timeout, unit);
	}


	  /**
	  * {@inheritDoc}
	  * 
	  * This implementation is equivalent to calling the equivalent
	  * <code>invokeAny()</code> method on the wrapped ExecutorService instance.
	  * 
	  * @param <T> return type of the function(s)
	  * @throws InterruptedException if interrupted while waiting, in which
	  *  case unfinished tasks are cancelled 
	  */
	public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
			throws InterruptedException, ExecutionException {
		return targetExec.invokeAny(tasks);
	}



	  /**
	  * {@inheritDoc}
	  * 
	  * This implementation is equivalent to calling the equivalent
	  * <code>invokeAny()</code> method on the wrapped ExecutorService instance.
	  * 
	  * @param <T> return type of the function(s)
	  * @throws InterruptedException if interrupted while waiting 
	  */
	public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
			long timeout, TimeUnit unit) throws InterruptedException,
			ExecutionException, TimeoutException {
		return targetExec.invokeAny(tasks, timeout, unit);
	}
    
}
